package org.streets.eis.component.grid;

import java.io.IOException;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.wicket.Application;
import org.apache.wicket.Component;
import org.apache.wicket.IRequestTarget;
import org.apache.wicket.IResourceListener;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.Session;
import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.WicketAjaxReference;
import org.apache.wicket.behavior.IBehavior;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.html.IHeaderResponse;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.WicketEventReference;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.FormComponent;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.html.resources.CompressedResourceReference;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.protocol.http.WebResponse;
import org.apache.wicket.protocol.http.WicketURLEncoder;
import org.apache.wicket.protocol.http.request.WebClientInfo;
import org.apache.wicket.util.io.ByteArrayOutputStream;
import org.apache.wicket.util.lang.PropertyResolver;
import org.apache.wicket.util.lang.PropertyResolverConverter;
import org.apache.wicket.util.string.Strings;
import org.streets.eis.component.WebResources;
import org.streets.eis.setting.validatior.TableListValidator;
import org.streets.eis.util.ExcelUtils;

import com.inmethod.grid.IDataSource;
import com.inmethod.grid.IGridColumn;
import com.inmethod.grid.column.AbstractColumn;
import com.inmethod.grid.column.PropertyColumn;
import com.inmethod.grid.common.AbstractGrid;
import com.inmethod.grid.datagrid.DataGrid;
import com.inmethod.grid.toolbar.AbstractToolbar;
import com.inmethod.grid.toolbar.NoRecordsToolbar;
import com.inmethod.grid.toolbar.paging.PagingToolbar;

/**
 * 1、三种model应用场景：
 * 
 * DefaultTableModel : 用来编辑已有数据,使用数据库
 * MemoryTableModel：用来展示或编辑内存数据,不使用数据库(不支持分页和过滤)
 * LoadableTableModel:用来只读展示数据,只缓存id(T必须包括名为id的Field)，使用数据库
 * 
 *  2、Column使用类型：
 *  
 *  CheckBoxColumn  checkbox选择器列(只做选择器，不做数据列，可使其单选)
 *  PropertyColumn  只读列
 *  EditableColumn   文本框编辑列,可以是String、Integer等各种类型
 *  CodeGroupColumn 代码组编辑列(包含id和text两个值) 
 *  DateTimeColumn  日期时间编辑列
 *  BooleanColumn		"是/否"编辑列
 *  ForeignComboBoxColumn 关联表编辑列(多表可用视图完成)(包含id和text两个值) 
 *  LinkColumn			功能链接列
 *  EditFlagColumn 切换行编辑状态
 *  ButtonedEditColumn  可弹出窗口的编辑组件(包含id和text两个值) 
 *  OrganComboBoxColumn  操作用户权限内机构下拉选择组件(包含id和text两个值) 
 *  
 *  注意：以上列中标明了"(包含id和text两个值)"的列 TableList对应的bean类中其propertyName的get/set方法签名应为ListItem类型
 *  
 *  如果有必要，你可以从EditableColumn继承实现你所需要的其他Column
 *  
 *   Column的propertyExpression为T的Field名字，如果该Field为自定义对象，要实现toString得到显示值
 *   Column的sortProperty为排序时用的名字，不排序字段用不需要sortProperty的构造器生成Column，对于数据库表，一般Column的sortProperty为物理字段名
 *   
 *   *******************************************重要须知*****************************************************************
 *   如果要编辑，对于代码组和关联表字段，T需要提供以propertyExpression为名字的类型为ListItem的public Field(或getter/setter)，TableList在生成编辑控件时自动存取此Field
 *   
 *   需要选择器或者在编辑情况下，在编辑框按Esc可以取消改行的编辑状态，对于有onchange或者其他改变Model的情况下：
 *			1)T要实现equals和hashCode，规则为id相等则对象相等（或其他唯一且不会被修改Model的Field)
 *			2)TableList要实现reloadRowObject方法，得到原来的数据   
 *  
 *  当本页数据发生变化(删除，刷新等)时，一定要清空所有已选行(resetSelectedItems方法)或者将某行置为未选(selectItem方法)
 *    
 *  FilterBar支持>,<,>=,<=,=符号（符号必须在第一位），若符号后的表达式非法（比如在数字字段里输入>a)，程序会自动转成like方式
 *  Filter条件须自己拼接，见列子
 *  
 */
public class TableList<T extends Serializable> extends DataGrid implements IResourceListener {

	private static final long serialVersionUID = 1L;
	
	public static class FilterCondition{
		public String operatorMode;
		public String value;
		
		public FilterCondition(String mode,String v){
			this.operatorMode = mode;
			this.value = v;
		}
	}
	
	public static class ExcelFile{
		public ByteArrayOutputStream workbook;
		public String fileName;
		
		public ExcelFile(String fileName,ByteArrayOutputStream workbook){
			this.fileName = fileName;
			this.workbook = workbook;
		}
	}
	
	public interface CreateExcelFileCallback extends Serializable{	
		ExcelFile createExcel();		
	}

	protected static final CompressedResourceReference CSS = new CompressedResourceReference(AbstractGrid.class,
		"res/style.css");

	private boolean canCancelEdit = true;
	
	private boolean editAllRowsOnInitialization = false;
	
	private boolean showNoRecordsBar = true;
	
	private boolean showPagingBar = true;	
	
	private boolean showFilterBar = false;
	
	private DefaultFilterToolbar filterBar;
	
	private IBehavior cancelEditBehavior;
	
	private AbstractToolbar noRecordsBar;
	
	private AbstractToolbar pagingBar;
	
	private transient ExcelFile excelFile;
	
	private CreateExcelFileCallback excelCallback;
	
	public TableList(String id, IModel<?> model, List<IGridColumn> columns) {
		super(id, model, columns);
		init();
	}

	public TableList(String id, IDataSource dataSource, List<IGridColumn> columns) {
		super(id, dataSource, columns);
		init();
	}
	
	public void renderHead(IHeaderResponse response) {
		response.renderJavascriptReference(WicketEventReference.INSTANCE);
		response.renderJavascriptReference(WicketAjaxReference.INSTANCE);
		response.renderJavascriptReference(WebResources.Yahoo.CORE_JS);
		response.renderJavascriptReference(WebResources.Yahoo.EVENT_JS);
		response.renderJavascriptReference(WebResources.Yahoo.DOM_JS);
		response.renderJavascriptReference(WebResources.InMethod.GRID_JS);
		response.renderCSSReference(CSS);
		if (!AbstractGrid.THEME_VISTA.equals(getTheme())){
			renderCustomCSSReference(response);
		}		
	}
	
	protected void renderCustomCSSReference(IHeaderResponse response) {
		response.renderCSSReference(new CompressedResourceReference(
				WebResources.class, "res/streets-Table-"+getTheme()+".css"));
	}
	
	protected void init() {
		setTheme("classic");
		setCleanSelectionOnPageChange(true);
		fixEmptyCellsRow();
	}
	
	/**
	 * 修正拖动列到最后时js出错的bug：少一个cell
	 */
	protected void fixEmptyCellsRow(){
		Component row = ((MarkupContainer)getForm().get("bodyContainer")).get("firstRow");
		
		StringBuilder emptyCells = new StringBuilder();
		for (int i = 0; i <= getActiveColumns().size(); ++i) {
			emptyCells.append("<td></td>");
		}
		row.replaceWith(new Label("firstRow", emptyCells.toString()).setEscapeModelStrings(false));	
	}
	
	@Override
	protected void onComponentTag(final ComponentTag tag){
		super.onComponentTag(tag);
		String className = tag.getString("class").toString();
		className = className.replaceAll("imxt-grid", "tableList");
		tag.put("class", className);
	}
	
	@Override
	protected void onBeforeRender(){
		super.onBeforeRender();
		if (canCancelEdit && cancelEditBehavior==null){
			add(cancelEditBehavior = newChangeEditBehavior());
		}
		if (showNoRecordsBar && noRecordsBar==null){
			addBottomToolbar(noRecordsBar = new NoRecordsToolbar(this,Model.of("没有可显示的记录!")));
		}
		
		if (showFilterBar && filterBar==null){
			addHeaderToolbar(filterBar = new DefaultFilterToolbar(this));
		}
		
		if (showPagingBar && pagingBar==null){
			addBottomToolbar(pagingBar = new PagingToolbar(this){
				private static final long serialVersionUID = 1L;
				@Override
				protected Component newNavigationLabel(String id) {
					return TableList.this.newNavigationLabel(id);
				}
				@Override
				protected Component newPagingNavigator(String id) {
					return TableList.this.newPagingNavigator(id);
				}
			});
		}
	}
	
	protected Component newNavigationLabel(String id) {
		return new DefualtNavigatorLabelPanel(id,this);
	}
	
	protected Component newPagingNavigator(String id) {
		return new DefaultPagingNavigator(id, this);
	}
	
	protected IBehavior newChangeEditBehavior(){
		return new AbstractDefaultAjaxBehavior(){
			private static final long serialVersionUID = 1L;

			@Override
			protected void onComponentTag(final ComponentTag tag){
				super.onComponentTag(tag);
				tag.put("changeEditUrl", this.getCallbackUrl(false));
			}
			@Override
			protected void respond(AjaxRequestTarget target) {
				String idx = getRequest().getParameter("idx");
				if (!Strings.isEmpty(idx)){
					IModel<T> rowModel = TableList.this.getRowModel(Integer.valueOf(idx));
					if (rowModel!=null){
						if (TableList.this.isItemEdited(rowModel)){
							TableList.this.setItemEdit(rowModel, false);
							rowModel.setObject(TableList.this.reloadRowObject(rowModel.getObject()));
						} else {
							TableList.this.setItemEdit(rowModel, true);
						}
						TableList.this.update();
					}
				} else {
					String cur = getRequest().getParameter("cur");
					if (!Strings.isEmpty(cur)){
						if ("0".equals(cur)){//want to edit all
							TableList.this.editAllRows();
						} else {
							TableList.this.cancelEditAllRows();
						}
						TableList.this.update();
					}
				}
			}			
		};
	}
	
	public boolean isCanCancelEdit() {
		return canCancelEdit;
	}

	public TableList<T> setCanCancelEdit(boolean canCancelEdit) {
		this.canCancelEdit = canCancelEdit;
		return this;
	}
	
	public boolean isEditAllRowsOnInitialization() {
		return editAllRowsOnInitialization;
	}

	public TableList<T> setEditAllRowsOnInitialization(boolean editAllRowsOnInitialization) {
		this.editAllRowsOnInitialization = editAllRowsOnInitialization;
		return this;
	}
	
	public boolean isShowNoRecordsBar() {
		return showNoRecordsBar;
	}

	public TableList<T> setShowNoRecordsBar(boolean showNoRecordsBar) {
		this.showNoRecordsBar = showNoRecordsBar;
		return this;
	}

	public boolean isShowPagingBar() {
		return showPagingBar;
	}

	public TableList<T> setShowPagingBar(boolean showPagingBar) {
		this.showPagingBar = showPagingBar;
		return this;
	}
	
	public boolean isShowFilterBar() {
		return showFilterBar;
	}

	public TableList<T> setShowFilterBar(boolean showFilterBar) {
		this.showFilterBar = showFilterBar;
		return this;
	}	
	
	/**
	 * 单引号已做转义
	 * @return
	 */
	public Map<IGridColumn,String> getFilterValues(){
		Map<IGridColumn,String> filters = new HashMap<IGridColumn,String>();
		if (filterBar!=null){
			Map<String,String> filterData = filterBar.getFilterValues();
			for (String key : filterData.keySet()){
				String value = filterData.get(key);
				if (!Strings.isEmpty(value)){
					if (value.indexOf("'")>=0){
						value = value.replaceAll("'", "''");
					}
					filters.put(getColumnById(key), value);
				}
			}
		}
		return filters;
	}
	
	/**
	 * 得到过滤字段的sql条件
	 * @return
	 */
	public Map<IGridColumn,String> getFilterSqls(){
		Map<IGridColumn,String> sqls = getFilterValues();
		for (IGridColumn column : sqls.keySet()){
			sqls.put(column, generateColumnFilterSql(column,parseFilterValue(sqls.get(column).trim())));
		}
		return sqls;
	}
	
	public IGridColumn getColumnById(String columnId){
		for (IGridColumn column : getAllColumns()) {
			if (column.getId().equals(columnId)) {
				return column;
			}
		}
		return null;
	}

	/**
	 * 解析过滤条件
	 * @param value
	 * @return
	 */
	public FilterCondition parseFilterValue(String value){
		String mode = "";
		String v = value;
		String[] keys = new String[]{">=","<=",">","<","="};
		
		for (int i=0;i<keys.length;i++){
			String key = keys[i];
			if (value.startsWith(key)){
				mode = key;
				v = value.substring(key.length());
				break;
			}
		}
		return new FilterCondition(mode,v);
	}
	
	/**
	 * 默认为列的排序名称作为字段名,使用like做全匹配，覆盖此方法可以对某字段做filter单独处理
	 * 支持>,<,>=,<=,=符号（符号必须在第一位），若符号后的表达式非法（比如在数字字段里输入>a)，程序会自动转成like方式
	 * @param column
	 * @param value
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public String generateColumnFilterSql(IGridColumn column,FilterCondition condition){
		Class<T> objectClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
		String columnExpr = getDBFieldName(column);

		try {
			Class<?> classType = PropertyResolver.getPropertyClass(column.getId(), objectClass.newInstance());
			
			if (Strings.isEmpty(condition.operatorMode)){ //默认按like处理
				if (java.sql.Date.class.isAssignableFrom(classType)){
					columnExpr = "to_char("+columnExpr+",'YYYY-MM-DD')";
				} else if (java.sql.Time.class.isAssignableFrom(classType)){
					columnExpr = "to_char("+columnExpr+",'HH24-MI-SS')";
				} else if (java.sql.Timestamp.class.isAssignableFrom(classType)){
					columnExpr = "to_char("+columnExpr+",'YYYY-MM-DD HH24-MI-SS')";
				}
			} else {
				PropertyResolverConverter prc = new PropertyResolverConverter(Application.get().getConverterLocator(),Session.get().getLocale());
				if (java.sql.Date.class.isAssignableFrom(classType)){
					try{
						Object o = prc.convert(condition.value, java.sql.Date.class);
						String v = (String) prc.convert(o,String.class);
						return columnExpr + condition.operatorMode + "to_date('" + v+ "','YYYY-MM-DD')"; 
					}catch (RuntimeException ex){
					}					
				} else if (java.sql.Time.class.isAssignableFrom(classType)){
					try{
						Object o = prc.convert(condition.value, java.sql.Time.class);
						String v = (String) prc.convert(o,String.class);
						return columnExpr + condition.operatorMode + "to_date('" + v+ "','HH24-MI-SS')";  
					}catch (RuntimeException ex){
					}
				} else if (java.sql.Timestamp.class.isAssignableFrom(classType)){
					try{
						Object o = prc.convert(condition.value, java.sql.Timestamp.class);
						String v = (String) prc.convert(o,String.class);
						return columnExpr + condition.operatorMode + "to_date('" + v+ "','YYYY-MM-DD HH24-MI-SS')"; 
					}catch (RuntimeException ex){
					}
				} else if (java.lang.Number.class.isAssignableFrom(classType)){
					try{
						Object o = prc.convert(condition.value, classType);
						String v = (String) prc.convert(o,String.class);
						return columnExpr + condition.operatorMode + v; 
					}catch (RuntimeException ex){
					}
				} else {
					return columnExpr + condition.operatorMode + "'" + condition.value+ "'"; 
				}
			}
		} catch (InstantiationException e) {
		} catch (IllegalAccessException e) {
		}
		return generateLikeSql(columnExpr,condition.value);
	}
	
	/**
	 * 若generateColumnFilterSql逻辑不用修改 也可覆盖此方法 提供一个数据库字段名字
	 * @param column
	 * @return
	 */
	public String getDBFieldName(IGridColumn column){
		return column.getSortProperty();
	}
	
	public String generateLikeSql(String field,String value){
		if (value.indexOf("%")>=0){
			return field + " like '%" + value.replaceAll("%", "/%") + "%' escape '/' ";
		} else {
			return field + " like '%" + value + "%'";
		}
	}
	
	/**
	 * 只用在编辑状态框中按ESC取消编辑恢复该行数据时
	 * 如果改行的Model已经被改变了，覆盖此方法恢复Model，若没有改变，不用覆盖此方法
	 * 注意：T类要继承hashCode和equals，让id相等的对象相等
	 * @param curValue
	 * @return
	 */
	protected T reloadRowObject(final T curValue){
		return curValue;
	}
	
	@SuppressWarnings("unchecked")
	public IModel<T> getRowModel(int index){
		WebMarkupContainer rows =  (WebMarkupContainer)get("form:bodyContainer:body:row");
		if (rows!=null){
			Component c = rows.get(index);
			return ( IModel<T>)c.getDefaultModel() ;
		} 
		return null;
	}
	
	@SuppressWarnings("unchecked")
	public List<IModel<T>> getRowModels(){
		WebMarkupContainer rows = (WebMarkupContainer)get("form:bodyContainer:body:row");
		List<IModel<T>> models = new ArrayList<IModel<T>>();
		if (rows != null) {
			for (Iterator<?> i = rows.iterator(); i.hasNext();) {
				Component component = (Component) i.next();
				models.add((IModel<T>)component.getDefaultModel());
			}
		}
		return models;
	}
	
	@SuppressWarnings("unchecked")
	public List<IModel<T>> getEditingRowModels(){
		WebMarkupContainer rows = (WebMarkupContainer)get("form:bodyContainer:body:row");
		List<IModel<T>> models = new ArrayList<IModel<T>>();
		if (rows != null) {
			for (Iterator<?> i = rows.iterator(); i.hasNext();) {
				Component component = (Component) i.next();
				IModel<T> rowModel = (IModel<T>)component.getDefaultModel();
				if (isItemEdited(rowModel)){
					models.add(rowModel);
				}
			}
		}
		return models;
	}
	
	@SuppressWarnings("unchecked")
	public List<T> getSelectedRowModelObjects(){
		WebMarkupContainer rows = (WebMarkupContainer)get("form:bodyContainer:body:row");
		List<T> results = new ArrayList<T>();
		if (rows != null) {
			for (Iterator<?> i = rows.iterator(); i.hasNext();) {
				Component component = (Component) i.next();
				IModel<T> rowModel = (IModel<T>)component.getDefaultModel();
				if (isItemSelected(rowModel)){
					results.add((T)rowModel.getObject());
				}
			}
		}
		return results;
	}
	
	@SuppressWarnings("unchecked")
	public List<T> getAllRowModelObjects(){
		WebMarkupContainer rows = (WebMarkupContainer)get("form:bodyContainer:body:row");
		List<T> results = new ArrayList<T>();
		if (rows != null) {
			for (Iterator<?> i = rows.iterator(); i.hasNext();) {
				Component component = (Component) i.next();
				IModel<T> rowModel = (IModel<T>)component.getDefaultModel();
				results.add((T)rowModel.getObject());
			}
		}
		return results;
	}
	
	/**
	 *  用在编辑提交时，放在包含grid的form的onError事件里 
	 */
	public void onValidateError(){
		onValidateError(null);
	}
	
	/**
	 *  一个TableListValidator实例不要用在多个TableList上
	 * @param validator
	 */
	public TableList<T> add(TableListValidator<T> validator){
		if (validator.getTableList()==null || validator.getTableList()==this){
			getForm().add(validator.setTableList(this));
		}
		return this;
	}
	
	/**
	 *  用在编辑提交时，放在包含grid的form的onError事件里 
	 */
	public void onValidateError(FeedbackPanel feedback){
		IRequestTarget requsetTarget = getRequestCycle().getRequestTarget();
		if (requsetTarget instanceof AjaxRequestTarget){
			final AjaxRequestTarget target = (AjaxRequestTarget) requsetTarget;
			if (feedback!=null) target.addComponent(feedback);
			
			getForm().visitChildren(FormComponent.class, new IVisitor<FormComponent<?>>(){
				public Object component(FormComponent<?> component){
					if (!component.isValid()){
						target.addComponent(component.getParent());
					}
					return IVisitor.CONTINUE_TRAVERSAL;
				}
			});
		}
	}
	
	/**
	 * 此刷新未清空选择项，若需要清空选择项（内容行发生变化了），须手动先调用resetSelectedItems方法
	 */
	public void ajaxRefresh(){
		refreshAllRows();
		markAllItemsDirty();
		update();
	}
	
	/**
	 * 刷新所有行数据
	 */
	public void refreshAllRows(){
		WebMarkupContainer rows = (WebMarkupContainer)get("form:bodyContainer:body:row");
		if (rows != null) {
			List<Component> deleteds = new ArrayList<Component>();
			for (Iterator<?> i = rows.iterator(); i.hasNext();) {
				Component component = (Component) i.next();
				deleteds.add(component);
			}
			for (Component component : deleteds){
				component.remove();
			}
		}
	}

	/**
	 * 刷新选中行数据
	 */
	@SuppressWarnings("unchecked")
	public void refreshSelectedRows(){
		WebMarkupContainer rows = (WebMarkupContainer)get("form:bodyContainer:body:row");
		if (rows != null) {
			List<Component> deleteds = new ArrayList<Component>();
			for (Iterator<?> i = rows.iterator(); i.hasNext();) {
				Component component = (Component) i.next();
				IModel<T> rowModel = (IModel<T>)component.getDefaultModel();
				if (isItemSelected(rowModel)){
					deleteds.add(component);
				}
			}
			for (Component component : deleteds){
				component.remove();
			}
		}
	}

	/**
	 * 刷新编辑行数据
	 */
	@SuppressWarnings("unchecked")
	public void refreshEditRows(){
		WebMarkupContainer rows = (WebMarkupContainer)get("form:bodyContainer:body:row");
		if (rows != null) {
			List<Component> deleteds = new ArrayList<Component>();
			for (Iterator<?> i = rows.iterator(); i.hasNext();) {
				Component component = (Component) i.next();
				IModel<T> rowModel = (IModel<T>)component.getDefaultModel();
				if (isItemEdited(rowModel)){
					deleteds.add(component);
				}
			}
			for (Component component : deleteds){
				component.remove();
			}
		}
	}
	
	public void editAllRows(){
		WebMarkupContainer rows = (WebMarkupContainer)get("form:bodyContainer:body:row");
		if (rows != null) {
			for (Iterator<?> i = rows.iterator(); i.hasNext();) {
				Component component = (Component) i.next();
				IModel<?> rowModel = (IModel<?>)component.getDefaultModel();
				if (!isItemEdited(rowModel)){
					setItemEdit(rowModel, true);
				}
			}
		}
	}
	
	@SuppressWarnings("unchecked")
	public void cancelEditAllRows(){
		WebMarkupContainer rows = (WebMarkupContainer)get("form:bodyContainer:body:row");
		if (rows != null) {
			for (Iterator<?> i = rows.iterator(); i.hasNext();) {
				Component component = (Component) i.next();
				IModel<T> rowModel = (IModel<T>)component.getDefaultModel();
				if (isItemEdited(rowModel)){
					setItemEdit(rowModel, false);
					rowModel.setObject(reloadRowObject(rowModel.getObject()));
				}
			}
		}
	}	
	
	@SuppressWarnings("unchecked")
	public void cancelEditSelectedRows(){
		for (Iterator iterator = getSelectedItems().iterator(); iterator.hasNext();) {
			IModel rowModel = (IModel) iterator.next();
			setItemEdit(rowModel, false);
			rowModel.setObject(reloadRowObject((T) rowModel.getObject()));
		}
	}
	
	@SuppressWarnings("unchecked")
	public void editSelectedRows(){
		for (Iterator iterator = getSelectedItems().iterator(); iterator.hasNext();) {
			IModel rowModel = (IModel) iterator.next();
			setItemEdit(rowModel, true);
		}
	}	
	
	@SuppressWarnings("unchecked")
	@Override
	public WebMarkupContainer findRowComponent(IModel rowModel) {
		return super.findRowComponent(rowModel);
	}

	public String getSaveAsExcelJavascript() {
		StringBuffer sb = new StringBuffer();
		sb.append("var _downloadFrame = document.getElementById(\"_$downloadFrame$_\");\n")
			.append("if (!_downloadFrame){\n")
			.append("	_downloadFrame = document.createElement(\"IFRAME\");\n")
			.append("	document.body.appendChild(_downloadFrame);\n")
			.append("	_downloadFrame.id=\"_$downloadFrame$_\";\n")
			.append("	_downloadFrame.name=\"_$downloadFrame$_\";\n")
			.append("	_downloadFrame.style.display=\"none\";\n")
			.append("	_downloadFrame.style.position=\"absolute\";\n")
			.append("}\n")
			.append("_downloadFrame.contentWindow.location.href=\"").append(urlFor(IResourceListener.INTERFACE)).append("\";");
		
			
		return sb.toString();
	}
	
	public void setCreateExcelCallback(CreateExcelFileCallback callback){
		this.excelCallback = callback;
	}
	
	public ExcelFile generateExcelFile(String fileName,String sheetName,List<T> records,List<IGridColumn> exportColumns){		
		return new ExcelFile(fileName,createExcelStream(generateExcelFileDatas(records,exportColumns),sheetName));
	}
	
	@SuppressWarnings("unchecked")
	public List<List<Object>> generateExcelFileDatas(List<T> records,List<IGridColumn> exportColumns){
		List<List<Object>> list = new ArrayList<List<Object>>();
		
		List<Object> head = new ArrayList<Object>();
		list.add(head);
		for (IGridColumn column : exportColumns){
			IModel colModel = ((AbstractColumn)column).getHeaderModel();
			if (colModel!=null) {
				head.add(colModel.getObject().toString());
			}
		}
		
		for (T object : records){			
			List<Object> rowData = new ArrayList<Object>();
			list.add(rowData);
			for (IGridColumn column : exportColumns){
				IModel colModel = ((AbstractColumn)column).getHeaderModel();
				if (colModel!=null) {
					String propertyExpression = column.getId();
					if (column instanceof PropertyColumn){
						propertyExpression = ((PropertyColumn) column).getPropertyExpression();
					}
					Object value = PropertyResolver.getValue(propertyExpression,object);
					rowData.add(value);
				}
			}
		}
		return list;
	}
	
	public ByteArrayOutputStream createExcelStream(List<List<Object>> list,String sheetName){
		return ExcelUtils.list2Excel(list, sheetName);
	}
	
	private void onResourceException(final String hint){
		WebResponse r = (WebResponse)getResponse();
		final String encoding = Application.get().getRequestCycleSettings().getResponseRequestEncoding();
		r.setCharacterEncoding(encoding);
		r.setContentType("text/html; charset=" + encoding);

		r.setHeader("Expires", "Mon, 26 Jul 1997 05:00:00 GMT");
		r.setHeader("Cache-Control", "no-cache, must-revalidate");
		r.setHeader("Pragma", "no-cache");
			
		r.write("<html><head>");
		r.write("<script>alert('"+hint+"');</script>");
		r.write("</head><body></body></html>");
	}

	private String encodeFileName(String fileName){
		WebClientInfo info = (WebClientInfo) Session.get().getClientInfo();
		
		if (info.getProperties().isBrowserMozilla()){
			try {
				return new String(fileName.getBytes("UTF-8"),"ISO8859-1");
			} catch (UnsupportedEncodingException e) {
			}					
		}
		return WicketURLEncoder.QUERY_INSTANCE.encode(fileName);
	}
	
	
	public void onResourceRequested(){		
		if (excelFile==null){
			if (excelCallback==null){
				onResourceException("没有生成Excel文件，请重新导出Excel");
				return;
			}
			excelFile = excelCallback.createExcel();
		}
		
		if (excelFile==null || excelFile.workbook == null){
			onResourceException("没有生成Excel文件，请重新导出Excel");
			return;
		}
				
		if (Strings.isEmpty(excelFile.fileName)) {
			excelFile.fileName = "Excel文件";
		}		

		String fn = encodeFileName(excelFile.fileName);
		
		WebResponse response = (WebResponse)getResponse();
		final String encoding = Application.get().getRequestCycleSettings().getResponseRequestEncoding();
		response.setCharacterEncoding(encoding);
		response.setHeader("Expires", "Mon, 26 Jul 1997 05:00:00 GMT");
		response.setHeader("Cache-Control", "no-cache, must-revalidate");
		response.setHeader("Pragma", "no-cache");
		
		response.setAttachmentHeader(fn);
		response.setContentType("application/vnd.ms-excel;charset="+encoding);
						
		try {				
			response.getOutputStream().write(excelFile.workbook.toByteArray());
			response.getOutputStream().close();
		} catch (IOException e) {
			e.printStackTrace();
			onResourceException("下载Excel文件错误，请重新导出Excel");
		}						

	}
}
